Javascript 함수 선언 방법과 호이스팅

JavaScript

함수 선언 방법

함수선언문 (Function Declaration)

// 일반적인 함수 선언 방법
function add(a, b) {
  return a + b
}

함수표현식 (Function expression)

// arrow function으로 함수표현식 선언 방법
const myFunc1 = () => {  } // 매개변수가 없을 경우
const myFunc2 = x => { } // 매개변수가 한 개인 경우, 소괄호를 생략 가능
const myFunc3 = (x, y) => { } // 매개변수가 여러 개인 경우, 소괄호를 생략 불가

// Body 전개 방법
const myFunc4 = x => { return x * 2 }  // single line block
const myFunc5 = x => x * 2             // 함수 몸체가 한줄의 구문이라면 중괄호 생략 가능 
const myFunc6 = () => { return { a: 1 }}  
const myFunc7 = () => ({ a: 1 }) // 위 표현과 동일. 객체 반환시는 소괄호를 사용.

호이스팅(hoisting)

예제 부터 보면

hoistFunc();
function hoistFunc(){ console.log('함수 선언이 아래에 있지만 문제 없어요') }

자바스크립트는 실행되기 전에 필요한 함수와 변수들을 모두 모아서 유효 범위의 최상단에 선언한다. 이를 호이스팅이라고 한다. hoisting

[명사] 끌어 올리기, 들어올려 나르기.

호이스팅 되는 놈 : 함수선언문, var (단, 선언만 끌어 올려지고 값은 끌어올려지지 않음)

호이스팅 안되는 놈 : 함수표현식, let, const

왜 안해줌?

파서가 안해줌.

개발 실수도 줄여주고 나쁘지 않은 방향. 되는 놈은 하위 호환 유지를 위해 여전히 지원하는것 뿐.

func1(message);  // 함수 func1과 message가 선언되기 전에 함수를 호출 하였지만 호이스팅됨.
function func1(message){
	console.log('your message is ' + message); // your message is undefined
}
var message = '안녕';  // 안녕 이라는 값은 호이스팅되지 않아 undefined로 출력된다.

func1 선언과 message 변수 선언이 호이스팅 되어 에러 없이 동작한다. 단, 값이 호이스팅 되지 않아 undefined 로 처리 된다.

// 변수를 선언하지 않는 경우
func2(msg); // Uncaught ReferenceError 발생
function func2(msg){
	console.log(msg);  
}

func2의 경우 호이스팅 되어 문제 없으나 msg 변수는 선언이 되지 않아 에러 발생한다.

console.log(name); // undefined
var name = 'hwan77';

// 위의 코드는 실제로 실행될 때 아래와 같이 처리됨.
var name; 
console.log(name); // undefined
name = 'hwan77';

function과 arrow function 의 차이점(3가지가 없는 놈)

1) 이름이 없다.

Arrow function은 익명 함수라는 점.

//일반 function
function myFunc(){
  console.log("hwan77")
}

//arrow function
() => {
  console.log("Nameless ㅠㅠ")
}
//참조해서 다른곳에서 쓰려면? const myFunc = () => {}

2) this가 없다.

Arrow function은 자신의 this가 없다.

arrow function은 외부에 있던 this를 그대로 내부로 가져와서 사용한다.

자바스크립트에서 this는 그 용법이 특이하다. 요약하자면 es5에서의 this는 이렇다.

  1. 함수 실행시에는 전역(window) 객체를 가리킨다.
  2. 메소드 실행시에는 메소드를 소유하고 있는 객체를 가리킨다.
  3. 생성자 실행시에는 새롭게 만들어진 객체를 가리킨다.
// this example
let myInfo={
   name: "hwan77",
   print: function(){
      console.log(this, this.name);
   }
};
myInfo.print(); // {name: 'hwan77', print: ƒ} 'hwan77'

let myInfo={
   name: "hwan77",
   print: () => {
      console.log(this, this.name);
   }
};
myInfo.print(); // window ''
const o = { 
	name: 'Kim', 
	speak: function () { 
		function getName() { return `${this.name}`; } // 여기서의 this는 window 객체이다. 함수 실행시에는 전역(window) 객체를 가리킨다
		return `My name is ${getName()}`; 
	} 
}; 

o.speak(); // "My name is "

const o = { 
	name: 'Kim', 
	speak: function() { 
		const getName = () => { return `${this.name}`; }  // 여기에서의 this는 o라는 객체
		return `My name is ${getName()}`; 
	} 
}; 

o.speak(); // "My name is Kim"
// arrow function은 맥락 상 위의 코드부터 보고 결합

3) arguments가 없다.

Arrow function은 arguments가 없다.

// 일반 function
function func1() {
  console.log(arguments[0]); // 1
  console.log(arguments[1]); // 2
  console.log(arguments[2]); // undefined
}

func1(1, 2);
// Arrow function
const myFunc2 = () => {
   console.log(arguments); // error
}

myFunc2(1, 2);

위 코드를 실행시켜보면

그래서 Arrow function의 경우 arguments를 추가 시켜주기 위해서는 전개 연산자를 활용해 arguments를 직접 명시해주어야 한다!

// 전개연산자 사용

const myFunc2 = (...arguments) => {
   console.log(arguments); // [1, 2, 3, 4]
}

myFunc2(1, 2, 3, 4);
다음 글: Javascript 템플릿 리터럴(Template literal) 이전 글: Javascript 비구조화 할당

See Also